public static class Quests
    {
        public static bool HasCompleted(uint id)
        {
            uint row = QuestCache.GetQuestCacheRowPtr(id);
            if (row != 0)
            {
                int index = Memory.Read<int>(row + 0x4);
                uint Array = Memory.AddBase((uint)Offsets.Quests.QuestV2);
                uint ArrayIndex = (((uint)index - 1) >> 3);

                byte UnkFlag1 = Memory.Read<byte>(Array + ArrayIndex);
                byte UnkFlag2 = (byte)(1 << ((index - 1) & 7));

                return (UnkFlag1 & UnkFlag2) > 0;
            }
            return false;
        }

        public static int NumQuestLogEntries
        {
            get
            {
                int entries = Memory.ReadRelative<int>((uint)Offsets.Quests.NumQuestLogEntries);
                return entries;
            }
        }

        public static bool HasQuest(uint id)
        {
            foreach (PlayerQuest qe in Globals.QuestLog)
            {
                if (qe.Id == id)
                {
                    return true;
                }
            }
            return false;
        }

        public static bool IsComplete(uint id)
        {
            foreach (PlayerQuest qe in Globals.QuestLog)
            {
                if (qe.Id == id)
                {
                    return qe.IsComplete;
                }
            }
            return false;
        }

        public static string GetQuestNameById(uint id)
        {
            foreach (PlayerQuest qe in Globals.QuestLog)
            {
                if (qe.Id == id)
                {
                    return qe.Name;
                }
            }
            return "";
        }

        public class PlayerQuest
        {
            private int index { get; set; }

            public PlayerQuest(int index)
            {
                this.index = index;
            }

            public bool IsHeader
            {
                get
                {
                    uint add = (uint)(index << 4);
                    return Memory.ReadRelative<uint>((uint)Offsets.Quests.IsHeader + add) != 0;
                }
            }

            public uint Id
            {
                get
                {
                    if (!IsHeader)
                    {
                        uint add = (uint)(index << 4);
                        return Memory.ReadRelative<uint>((uint)Offsets.Quests.QuestId + add);
                    }
                    return 0;
                }
            }

            private uint RowPtr
            {
                get
                {
                    if (!IsHeader)
                    {
                        return QuestCache.GetQuestCacheRowPtr(Id);
                    }
                    return 0;
                }
            }

            private int NumRewards
            {
                get
                {
                    if (!IsHeader)
                    {
                        uint off1 = Memory.Read<uint>(RowPtr + (uint)Offsets.Quests.NumRewardsOff1);
                        if (off1 != 0)
                        {
                            return Memory.Read<int>(off1 + (uint)Offsets.Quests.NumRewardsOff2);
                        }
                    }
                    return 0;
                }
            }

            public string Name
            {
                get
                {
                    if (RowPtr != 0)
                    {
                        return Memory.Read<string>(RowPtr + (uint)Offsets.Quests.QuestName);
                    }
                    return "";
                }
            }

            #region IsComplete

            private int NumObjectives
            {
                get
                {
                    return Memory.Read<int>(RowPtr + (uint)Offsets.Quests.ObjectivePtr);
                }
            }

            public bool IsComplete
            {
                get
                {
                    uint firstObjective = Memory.Read<uint>(RowPtr + (uint)Offsets.Quests.FirstObjPtr);
                    uint objectiveEnd = firstObjective + (uint)Offsets.Quests.ObjectiveSize * (uint)NumObjectives;
                    if (firstObjective == objectiveEnd)
                    {
                        return true;
                    }
                    for (uint i = firstObjective; i != objectiveEnd; i += (uint)Offsets.Quests.ObjectiveSize)
                    {
                        byte type = Memory.Read<byte>(i + 0x4);
                        if (IsObjectiveComplete(type, i) == 0)
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }

            private short HasKilled(uint me, uint id, uint i)
            {
                if (id != 0)
                {
                    uint counter = 0;
                    uint ptr = Memory.Read<uint>(me + 0x1330) + 0x2C;
                    while (Memory.Read<uint>(ptr) != id)
                    {
                        ++counter;
                        ptr += 0x3C;
                        if (counter >= 0x32)
                        {
                            return 0;
                        }
                    }
                    return Memory.Read<short>(ptr + 0x2 * (uint)(Memory.Read<byte>(i + 0x5)) + 0x8);
                }
                else
                {
                    return 0;
                }
            }

            private uint ObjectiveType4(uint a1, uint a2)
            {
                uint v2 = Memory.ReadRelative<uint>((uint)Offsets.Quests.ObjectiveType4);
                uint v3 = 0;
                while (true)
                {
                    if ((v2 & 1) != 0 || v2 == 0)
                    {
                        return 0;
                    }
                    v3 = Memory.Read<uint>(v2 + 0x8);
                    if (Memory.Read<uint>(v3) == a1)
                    {
                        break;
                    }
                    v2 = Memory.Read<uint>(v2 + 0x4);
                }
                uint v5 = Memory.Read<uint>(v2 + 0xC);
                if ((Memory.Read<byte>(v3 + 0x24) & 8) != 0)
                {
                    v5 *= 100;
                }
                return (uint)(v5 >= a2 ? 1 : 0);
            }

            private uint ObjectiveType1011(uint a1, uint id)
            {
                int count = 0;
                uint ptr = Memory.Read<uint>(a1) + 0x2C;
                while (Memory.Read<uint>(ptr) != id)
                {
                    ++count;
                    ptr += 0x3C;
                    if (count >= 0x32)
                    {
                        return 0;
                    }
                }
                return ptr;
            }

            private int IsObjectiveComplete(byte type, uint i)
            {
                uint objectiveRequired = Memory.Read<uint>(i + 0xC);
                uint objectiveitem = Memory.Read<uint>(i + 0x8);
                uint row = QuestCache.GetQuestCacheRowPtr(objectiveitem);
                switch (type)
                {
                    case 0: //kill amount
                    case 2:
                    case 3:
                    case 9:
                        return HasKilled(ObjectManager.Me.BaseAdress, Id, i) >= objectiveRequired ? 1 : 0;

                    case 1: //collect item
                        return ObjectManager.ItemsCount((int)objectiveitem) >= objectiveRequired ? 1 : 0;

                    case 8: //money objectives
                        return ObjectManager.Me.GetCoinage >= objectiveRequired ? 1 : 0;

                    case 4: //unknown
                        return (int)ObjectiveType4(objectiveitem, objectiveRequired);

                    case 10:
                    case 11: //unknown
                        uint v8 = ObjectiveType1011(ObjectManager.Me.BaseAdress + 0x1368, Id);
                        if (v8 == 0)
                        {
                            return 1;
                        }
                        return (int)((Memory.Read<uint>(v8 + 0x4) & (256 << Memory.Read<byte>(i + 0x5))) != 0 ? 1 : 0);

                    case 6: //Reputation Objectives
                        return row >= objectiveRequired ? 1 : 0;

                    case 7:
                        return row >= objectiveRequired ? 1 : 0;

                    case 5: //Know spell
                        return Spells.KnownSpells.ContainsKey(Memory.Read<int>(i + 0x8)) ? 1 : 0;

                    default:
                        return 1;
                }
            }
            #endregion
        }
    }